/*
 * Copyright 2013-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.gateway.route;

import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.Assert;

/**
 *RouteRefreshListener 是 Spring Cloud Gateway 中的一个事件监听器，用于监听和处理与路由刷新相关的事件。它在动态路由场景中非常有用，尤其是在结合 Spring Cloud Config 或其他外部配置服务时，可以实现无需重启网关即可动态更新路由配置。
 *
 * 1. 基本概念
 * 		动态路由：在运行时根据外部配置（如数据库、配置中心）动态更新路由规则，而不需要重启应用。
 * 		事件驱动机制：Spring Cloud 使用事件驱动的方式通知组件发生变化。例如，当配置文件被修改时，会触发 RefreshEvent 事件。
 * 		RouteRefreshListener：监听特定的事件（如 RefreshEvent），并触发路由重新加载逻辑。
 *
 * 2. 核心功能
 * 		RouteRefreshListener 的主要职责是：
 * 			监听事件：监听 RefreshEvent 或其他相关事件。
 * 			触发路由刷新：当接收到事件时，调用 RouteDefinitionLocator 重新加载路由定义，并更新到 RouteLocator 中。
 * 			支持动态更新：使得路由规则可以在运行时动态调整，而无需重启网关。
 * 3. 工作原理
 * 		3.1 事件监听
 * 		RouteRefreshListener 实现了 ApplicationListener<RefreshEvent> 接口，专门监听 RefreshEvent 事件。
 *
 * 		public class RouteRefreshListener implements ApplicationListener<RefreshEvent> {
 *     private final ApplicationEventPublisher publisher;
 *     public RouteRefreshListener(ApplicationEventPublisher publisher) {
 *         this.publisher = publisher;
 *     }
 *     @Override
 *     public void onApplicationEvent(RefreshEvent event) {
 *         // 触发路由刷新逻辑
 *         publisher.publishEvent(new RefreshRoutesEvent(this));
 *     }
 * }
 *
 */
// see ZuulDiscoveryRefreshListener
// TODO: make abstract class in commons?
public class RouteRefreshListener implements ApplicationListener<ApplicationEvent> {

	private final ApplicationEventPublisher publisher;

	private HeartbeatMonitor monitor = new HeartbeatMonitor();

	public RouteRefreshListener(ApplicationEventPublisher publisher) {
		Assert.notNull(publisher, "publisher may not be null");
		this.publisher = publisher;
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ContextRefreshedEvent) {
			ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent) event;
			if (!WebServerApplicationContext.hasServerNamespace(refreshedEvent.getApplicationContext(), "management")) {
				reset();
			}
		}
		else if (event instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) {
			reset();
		}
		else if (event instanceof ParentHeartbeatEvent) {
			ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
			resetIfNeeded(e.getValue());
		}
		else if (event instanceof HeartbeatEvent) {
			HeartbeatEvent e = (HeartbeatEvent) event;
			resetIfNeeded(e.getValue());
		}
	}

	private void resetIfNeeded(Object value) {
		if (this.monitor.update(value)) {
			reset();
		}
	}

	private void reset() {
		this.publisher.publishEvent(new RefreshRoutesEvent(this));
	}

}
